<?php

include("config.php");
include("inc/function.php");

function usage($msg=""){
	$msg = <<<USA
$msg
Usage: ab.php -n <requests> -c <concurrency> -u <url>
Options are:
	-n requests     Number of requests to perform
	-c concurrency  Number of multiple requests to make
	-u url

USA;
	die($msg);
}

$options = getopt("n:c:u:");

if( !is_array($options) 
	|| !@is_numeric($options['n']) 
	|| !@is_numeric($options['c'])) {
	usage();
}

if(empty($options['u'])) usage();

$opt_url = $options['u'];
$opt_requests = intval($options['n']);
$opt_concurrency = $options['c'];

if( $opt_concurrency > $g_cpu_count * 2 * $g_fd_setsize ) {
	usage("WOW, Sorry. The concurrency is too big");
}

//global vars

//main process
$g_pids = array();
$g_workers_count = $g_cpu_count * 2;
$g_readly_workers = 0;
$g_main_pid = posix_getpid();
$g_main_msgtype = 1;

//child process
$g_worker_pid = 0; 
$g_worker_stop = 0; //!sigterm

//sig
pcntl_signal(SIGTERM, "sigterm_main_handler");

//fork children
for($i = 0; $i < $g_workers_count ; $i++) {
	$g_worker_msgtype = $i + 3;
	$pid = pcntl_fork();
	if($pid == -1) {
		die("fork error\n");
	}
	elseif($pid == 0) {
		//worker
		worker_do();
		exit();
	}
	else {
		//main
		$g_pids[] = $pid;
	}
}

//msg
$g_msg = new ProcMessage($g_main_pid);

//wait for children readly
while(1) {
	//echo "main : wait workers readly ($g_readly_workers < $g_workers_count) \n";

	$r = $g_msg->receive($g_main_msgtype);
	if(empty($r)) {
		usleep(1000);
		continue;
	}

	if($r['data'] == 'readly') $g_readly_workers ++;
	
	if($g_readly_workers == $g_workers_count) {
		//echo "main : all workers is ready\n";
		break;
	}
}

//send 'begin' to children
for($i = 0; $i < $g_workers_count ; $i++) {
	$g_msg->send('begin', $i+3);
}

//wait children
foreach($g_pids as $pid) {
	pcntl_waitpid($pid, $status);
}

//receive stat message
$num = $g_workers_count;
$stats = array();
while($num){
	$r = $g_msg->receive(0);
	if(is_array($r) && $r['data'][0]=='stat') {
		$stats [] = $r['data'][1];
		$num--;
	}
	usleep(1000);
}
$g_msg->destruct();

//analyzer children output
$stat_result = array(
		'tunnels'	=>  0 ,
		'number'	=>	0 ,
		'begin_time'	=>  time(),
		'end_time'		=>  0 ,
		'http_code_2xx' => 0 ,
		'http_code_non_2xx' => 0 ,
		'connect_total' => 0 ,
		'connect_min'	=> 1000000 ,
		'connect_max'	=> 0 ,
		'request_total' => 0 ,
		'request_min'	=> 1000000 ,
		'request_max'	=> 0 ,
		'Requests per second' =>0 ,
	);
foreach($stats as $stat) {
	$stat_result['tunnels'] += $stat['tunnels'];
	$stat_result['number'] += $stat['number'];
	$stat_result['http_code_2xx'] += $stat['http_code_2xx'];
	$stat_result['http_code_non_2xx'] += $stat['http_code_non_2xx'];
	$stat_result['connect_total'] += $stat['connect_total'];
	$stat_result['request_total'] += $stat['request_total'];

	if($stat_result['begin_time'] > $stat['begin_time']) {
		$stat_result['begin_time'] = $stat['begin_time'];
	}
	if($stat_result['end_time'] < $stat['end_time']) {
		$stat_result['end_time'] = $stat['end_time'];
	}

	if($stat_result['connect_min'] > $stat['connect_min']) {
		$stat_result['connect_min'] = $stat['connect_min'];
	}
	if($stat_result['connect_max'] < $stat['connect_max']) {
		$stat_result['connect_max'] = $stat['connect_max'];
	}

	if($stat_result['request_min'] > $stat['request_min']) {
		$stat_result['request_min'] = $stat['request_min'];
	}
	if($stat_result['request_max'] < $stat['request_max']) {
		$stat_result['request_max'] = $stat['request_max'];
	}

	$stat_result['Requests per second'] += $stat['Requests per second'];
}

$stat_result['request_mean'] = $stat_result['request_total'] / $stat_result['number'];
$stat_result['Requests per second'] = $stat_result['number'] 
										/ ($stat_result['end_time'] - $stat_result['begin_time'] ) 
										;

echo "\nResult:\n";
foreach($stat_result as $key => $value) {
	echo "\t$key\t:\t$value\n";
}
exit(0);

